package cn.webestar.scms.security;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;

import java.time.LocalDateTime;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * @author zgs
 */
@Slf4j
public class SessionContextHolder {

    private static final InheritableThreadLocal<SessionContext> contexts = new InheritableThreadLocal<>();
    private static RedisTemplate<String, Object> redisTemplate;
    private static SecurityProperties properties;

    public static void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
        SessionContextHolder.redisTemplate = redisTemplate;
    }

    public static void setProperties(SecurityProperties properties) {
        SessionContextHolder.properties = properties;
    }

    public static SessionContext getContext() {
        return contexts.get();
    }

    public static void setContext(SessionContext context) {
        contexts.set(context);
        refresh();
    }

    public static Authentication getAuthentication() {
        SessionContext context = getContext();
        return context == null ? null : context.getAuth();
    }

    public static SecurityUser getPrincipal() {
        Authentication auth = getAuthentication();
        return auth == null ? null : auth.getPrincipal();
    }

    public static void refresh() {
        SessionContext context = contexts.get();
        String tokenKey = String.format(Constant.ACCESS_TOKEN_CACHE_KEY, properties.getCachePrefix(), context.getAuth().getId(), context.getAccessToken());
        String refreshTokenKey = String.format(Constant.REFRESH_TOKEN_CACHE_KEY, properties.getCachePrefix(), context.getAuth().getId(), context.getRefreshToken());
        if (redisTemplate != null) {
            redisTemplate.opsForValue().set(tokenKey, context, properties.getAccessTokenExpireIn(), TimeUnit.SECONDS);
            redisTemplate.expire(refreshTokenKey, properties.getAccessTokenExpireIn() * 2, TimeUnit.SECONDS);
        }
    }

    /**
     * 此方法已废弃，使用SessionContextHolder.auth()方法代替
     */
    @Deprecated
    public static SessionContext setAuthentication(Authentication authentication) {
        return auth(authentication);
    }

    public static SessionContext auth(Authentication authentication) {

        String id = authentication.getId();
        String accessToken = JwtUtils.generateToken(id, properties.getAccessTokenExpireIn());

        String refreshToken = UUID.randomUUID().toString().replace("-", "");

        SessionContext context = new SessionContext();
        context.setAccessToken(accessToken);
        context.setRefreshToken(refreshToken);
        context.setAuth(authentication);
        context.setExpire(LocalDateTime.now().plusSeconds(properties.getAccessTokenExpireIn()));

        SessionContextHolder.setContext(context);

        if (redisTemplate != null) {
            redisTemplate.opsForValue().set(String.format(Constant.ACCESS_TOKEN_CACHE_KEY, properties.getCachePrefix(), id, accessToken), context, properties.getAccessTokenExpireIn(), TimeUnit.SECONDS);
            redisTemplate.opsForValue().set(String.format(Constant.REFRESH_TOKEN_CACHE_KEY, properties.getCachePrefix(), id, refreshToken), accessToken, properties.getAccessTokenExpireIn(), TimeUnit.SECONDS);
        }

        return context;
    }

    public static void logout() {
        SessionContext context = SessionContextHolder.getContext();
        String id = context.getAuth().getId();
        String accessToken = context.getAccessToken();
        String refreshToken = context.getRefreshToken();
        if (redisTemplate != null) {
            redisTemplate.expire(String.format(Constant.ACCESS_TOKEN_CACHE_KEY, properties.getCachePrefix(), id, accessToken), 0, TimeUnit.SECONDS);
            redisTemplate.expire(String.format(Constant.REFRESH_TOKEN_CACHE_KEY, properties.getCachePrefix(), id, refreshToken), 0, TimeUnit.SECONDS);
        }
    }

    public static void invalid(Long userId) {

    }

    /**
     * 临时方法，解决消息推送、job执行时，手动设置租户上下文
     */
    public static void setCid(Long cid) {
        SecurityUser securityUser = new SecurityUser();
        securityUser.setCid(cid);
        Authentication auth = new Authentication();
        auth.setPrincipal(securityUser);
        SessionContext context = new SessionContext();
        context.setAuth(auth);
        SessionContextHolder.setContext(context);
    }

}